// JS validator,Copyright (c) 2008 Arian Stolwijk, <http://aryweb.nl>, MIT Style License.

function jsValidator(){
	var Validator = {
	
		/**
		 * Version of the jsValidator
		 */
		varsion : '0.5a',
	
		/**
		 * Settings
		 */
		settings: {
			/**
			 * The standard message
			 */
			msg: 'This field is not correctly filled in',
	
			/**
			 * The style of the message, any css/javascript style (eg. color, paddingLeft)
			 */
			msgStyle: {
				paddingLeft: '5px',
				color: 'red',
				maxWidth: '300px'
			}
		},
	
	
		elmts : {
			// The Elements
		},	
		elmt: {
			// The field objects
		},
	
		/**
		 * Initializes the validator
		 */
		init: function(){
			for (var elem in this.elmts) { //for each element....
				// Skip to the next if the field doesn't extists
				if(document.getElementById(elem) == null){
					continue;
				}
				if(document.getElementById(elem).tagName == 'input' || document.getElementById(elem).tagName == 'select' || document.getElementById(elem).tagName == 'textarea'){
					continue;
				}
				this.elmt[elem] = document.getElementById(elem);
				if (typeof this.elmts[elem].validators != 'undefined') {					
					this.addValidators(elem);
				}else {
					this.addValidator(elem);
				}
						
			}
		},
		
		/**
		 * Add a validator to a field, checks if the field is correct and calls the message functions
		 * @param {Object} elmt
		 */	
		addValidator: function(elem){
			this.elmt[elem].onblur = function(){
				if (typeof Validator.elmts[elem].validator != 'undefined' ) {					
					// Check if the field is correctly set
					var result = Validator.validate(elem, Validator.elmts[elem].validator);
					if (result == false) {
						// check if the message is set 
						if (Validator.elmts[elem].msg) {
							var msg = Validator.elmts[elem].msg;
						}else {
							var msg = Validator.settings.msg;
						}
						Validator.inlineMsg(elem, msg);
						return; // Stop the function, one error is enough
					}else if (result == 'undefined'){
					}else{
						Validator.hideMsg1(elem);
					}
				}
				if (typeof Validator.elmts[elem].regex != 'undefined') {
					// Validate the regex
					Validator.validateRegex(elem);
				}
			}
		},

		/**
		 * Add more than one validators to a field. 
		 * @param {Object} elem
		 */
		addValidators: function(elem){
			if(typeof this.elmts[elem].validators == 'object'){
				this.elmt[elem].onblur = function(){
					for (var validator in Validator.elmts[elem].validators) {
						var result = Validator.validate(elem,validator); 
						if (result == false) {
							// Validator.elmts[elem].validators[validator]
							if (Validator.elmts[elem].validators[validator].msg) {
								var msg = Validator.elmts[elem].validators[validator].msg;
							}
							else 
								if (Validator.elmts[elem].msg) {
									var msg = Validator.elmts[elem].msg;
								}
								else {
									var msg = Validator.settings.msg;
								}
							
							Validator.inlineMsg(elem, msg);
							return; // Stop the function, one error is enough
						}else if(result == 'undefined'){
						}else{
							// Everything went well
							Validator.hideMsg1(elem);
						}						
					}					
					// Validate the regex 
					Validator.validateRegex(elem);
				}
			}
		},

		/**
		 * 
		 * @param {Object} elem
		 * @param {Object} validator
		 * @param {Object} regex
		 */
		validate: function(elem,validator,regex){
			if (this.elmt[elem].tagName == 'SELECT') {
				value = this.elmt[elem].options[Validator.elmt[elem].selectedIndex].value;
			} else {
				value = this.elmt[elem].value;
			}
			// Set valid to false
			var valid = false;
			if (regex && validator == 'regex') {						
				// If there's a self made regex
				value = value+'';
				if (value.match(regex)) {
					// if the field is correct, set the valid var to true
					valid = true;
				}
			}else if(typeof Validator.elmts[elem].validator == 'function'){
				// There's a self made funtion
				if(Validator.elmts[elem].validator(value)){
					// if the field is correct, set the valid var to true
					valid = true;
				}
			}else {
				// The included validate methods
				if (typeof Validators[validator] == 'function') {
					if (Validators[validator](value + '')) {
						// if the field is correct, set the valid var to true
						valid = true;
					}
				}else{
					valid = 'undefined';
				}
			}
			return valid;
		},
		
		validateRegex: function(elem){
			// When there's a regex set for this validator, 
			// then check if there's a regex set for the field
			// When it isn't, set it to null
			msg = true;
			if (typeof this.elmts[elem].regex[0] == 'object') {
				var regexObj = this.elmts[elem].regex;
				for(var i=0;i<regexObj.length;i++){					
					if(typeof this.elmts[elem].regex[i] == 'object'){
						if(this.validate(elem,'regex',this.elmts[elem].regex[i].regex) == false){
							
							// check if the message is set 
							if(Validator.elmts[elem].regex[i].msg){
								var msg = Validator.elmts[elem].regex[i].msg;
							}else if(Validator.elmts[elem].msg){
								var msg = Validator.elmts[elem].msg;
							}else{
								var msg = Validator.settings.msg;
							}
							break;
						}				
					}					
				}
			}else {
				if (this.validate(elem, 'regex', this.elmts[elem].regex) == false) {
					if (Validator.elmts[elem].msg) {
						var msg = Validator.elmts[elem].msg;
					}else {
						var msg = Validator.settings.msg;
					}
				}
			}
			if(typeof msg == 'boolean' && msg == true){
				Validator.hideMsg1(elem);
			}else if(typeof msg == 'string'){
				Validator.inlineMsg(elem,msg);
			}
		},
			
		/**
		 * Build the message and calls the function to show the message
		 * 
		 * @param {Object} elem
		 * @param {Object} string
		 */
		inlineMsg: function(elem,string) {
			if (string != false) {
				var targetdiv = document.getElementById(elem);
				if (!document.getElementById('msg' + elem)) {
					// Create two divs for the error message if the divs doesn't exists
					msg = document.createElement('span');
					msg.id = 'msg' + elem;
					
					// give the msg div some style 
					for (var msgStyle in this.settings.msgStyle) {
						msg.style[msgStyle] = this.settings.msgStyle[msgStyle];
					}
					
					// add the msg element after the field
					targetdiv.parentNode.insertBefore(msg, targetdiv.nextSibling);
				}
				else {
					// The span elements already exists, so we can use them
					msg = document.getElementById('msg' + elem);
				}
				// Put the error message into the messagecontent element
				msg.innerHTML = string;
				// Show the message
				
			}else{
				msg = '';
			}
			this.showMsg(elem,msg);
		},
		
		// This function calls the hideMsg method to create the same parameters like the showMsg method
		hideMsg1: function(elem){
			var elmt = document.getElementById('msg'+elem);
			this.hideMsg(elem,elmt);
		},
	
		/**
		 * You can override this function with your own function 
		 * This function shows the message
		 *
		 * @param {String}elem : the fieldname
		 * @param {Object}elmt : an instance of the message element
		 */
		showMsg: function(elem,elmt){
			if (elmt) {
				elmt.style.display = 'inline';
			}
		},
		
		
		/**
		 * This method hides the elemnt
		 * 
		 * @param {String}elem : the fieldname
		 * @param {Object} elmt : an instance of the message element
		 */
		hideMsg: function(elem,elmt) {
			if (elmt) {
				// Hide the element when the element exists
				elmt.style.display = 'none';
			}
		}
	}
	
	return Validator;
}

/**
 * An apart object for the validation objects
 */
var Validators = {
	
	/**
	 * Validators.IsString()
	 *
	 * Any string that doesn't have control characters (ASCII 0 - 31) but spaces are allowed
	 *
	 * @param string value: The string to check
	 * @return bool
	*/
	IsString: function(value){
 		return value.match(/^[^\x01-\x1F]+$/);
	},

	/**
	 * Validators._IsString()
	 *
	 * Public: same as IsString, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsString: function(value)
	{
	    return value.length == 0 || this.IsString(value);
	},
	
	/**
	 * Validators.IsAlpha()
	 *
	 * Public: only letters a-z and A-Z
	 *
	 * @param string value
	 * @return bool
	 */
	IsAlpha: function(value)
	{
	    return value.match(/^[a-z]+$/i);
	},
	
	/**
	 * Validators._IsAlpha()
	 *
	 * Public: same as IsAlpha, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsAlpha: function(value)
	{
	    return value.length == 0 || this.IsAlpha(value);
	},
	
	/**
	 * Validators.IsDigit()
	 *
	 * Public: only numbers 0-9
	 *
	 * @param string value
	 * @return bool
	 */
	IsDigit: function(value)
	{
	    return value.match(/^[0-9]+$/);
	},
	
	/**
	 * Validators._IsDigit()
	 *
	 * Public: same as IsDigit, only now the value is also valid if it is empty
	 *
	 * @param string $value
	 * @return bool
	 */
	_IsDigit: function(value)
	{
	    return (value.length == 0 || this.IsDigit(value));
	},
	
	/**
	 * Validators.IsAlphaNum()
	 *
	 * Public: letters and numbers
	 *
	 * @param string value
	 * @return bool
	 */
	IsAlphaNum: function(value)
	{
	    return value.match(/^[a-z0-9]+$/i);
	},
	
	/**
	 * Validators._IsAlphaNum()
	 *
	 * Public: same as IsAlphaNum, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsAlphaNum: function(value)
	{
	    return value.length == 0 || this.IsAlphaNum(value);
	},
	
	/**
	 * Validators.IsFloat()
	 *
	 * Public: only numbers 0-9 and an optional - (minus) sign (in the beginning only)
	 *
	 * @param string value
	 * @return bool
	 */
	IsFloat: function(value)
	{
	    return value.match(/^-?([0-9]*\.?,?[0-9]+)$/);
	},
	
	/**
	 * Validators._IsFloat()
	 *
	 * Public: same as IsFloat, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsFloat: function(value){
		return value.length == 0 || this.IsFloat(value);
	},
	
	/** Validators.IsInteger()
	 *
	 * Public: only numbers 0-9 and an optional - (minus) sign (in the beginning only)
	 *
	 * @param string $value
	 * @return bool
	 */
	IsInteger: function(value)
	{
	    return value.match(/^-?[0-9]+$/);
	},
	
	/**
	 * Validators._IsInteger()
	 *
	 * Public: same as IsInteger, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsInteger: function(value)
	{
	    return value.length == 0 || this.IsInteger(value);
	},
	
	/**
	 * Validators.IsFilename()
	 *
	 * Public: a valid file name (including dots but no slashes and other forbidden characters)
	 *
	 * @param string value
	 * @return bool
	 * @TODO: This method doesn't work
	 */
/*
	IsFilename: function(value)
	{
		return value.match(/{^[^\/\*\?\:\,]+$}/);
	},
*/	
	/**
	 * Validators._IsFilename()
	 *
	 * Public: same as IsFilename, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
/*
	_IsFilename: function(value)
	{
		return value.length == 0 || this.IsFilename(value);
	},
*/	
	/**
	 * Validators.IsBool()
	 *
	 * Public: a boolean (case-insensitive "true"/"1" or "false"/"0")
	 *
	 * @param string value
	 * @return bool
	 */
	IsBool: function(value)
	{
	    return value.match(/^true$|^1|^false|^0$/i);
	},
	
	/**
	 * Validators._IsBool()
	 *
	 * Public: same as IsBool, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsBool: function(value)
	{
		return value.length == 0 || this.IsBool(value);
	},
	
	/**
	 * a valid variable name (letters, digits, underscore)
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsVariabele: function(value)
	{
	    if(value == '_')
	    {
	        return false;
	    }
	    else if(value.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/i))
	    {
	        return true;
	    }
	    else
	    {
	        return false;
	    }
	},
	
	/**
	 * same as IsVariabele, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsVariabele: function(value)
	{
		return value.length == 0 || this.IsVariabele(value);
	},
	
	
	/**
	 * a valid password (alphanumberic + some other characters but no spaces. Only allow ASCII 33 - 126)
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsPassword: function(value)
	{
	    return value.match(/^[\x29-\x7E]+$/);
	},
	
	/**
	 * same as IsVariabele, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsPassword: function(value)
	{
		return value.length == 0 || this.IsPassword(value);
	},

	/**
	 * check for a valid url
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsURL: function(value)
	{
	    regex = /^((http|ftp|https):\/{2})?(([0-9a-zA-Z_-]+\.)+[a-zA-Z]+)((:[0-9]+)?)((\/([0-9a-zA-Z=%\.\/_-]+)?(\?[0-9a-zA-Z%\/&=_-]+)?)?)$/;
	    return value.match(regex); 
	},
	
	/**
	 * same as IsURL, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsURL: function(value)
	{
		return value.length == 0 || this.IsURL(value);
	},
	
	/**
	 * a valid email address (only checks for valid format: xxx@xxx.xxx)
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsEmail: function(value)
	{
    	return value.match(/^[a-z0-9_\.-]+@([a-z0-9]+([\-]+[a-z0-9]+)*\.)+[a-z]{2,7}$/i);
	},
	
	/**
	 * same as IsEmail, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsEmail: function(value)
	{
		return value.length == 0 || this.IsEmail(value);
	},
			
	/**
	 * like IsString, but newline characters and tabs are allowed
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsText: function(value)
	{
	    return value.match(/^([^\x01-\x1F]|[\r\n\t])+$/);
	},
	
	/**
	 * same as IsText, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsText: function(value)
	{
			return value.length == 0|| this.IsText(value);
	},
	
	/**
	 * Check if the value does not contain any HTML
	 * 
	 * @param {string} value
	 * @return bool
	 */
	NoHTML: function(value)
	{
		return value.replace(/(<([^>]+)>)/ig,"") == value && value.length > 0;
	},
	
	/**
	 * same as NoHTML, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_NoHTML: function(value){
		return value.length == 0 || this.NoHTML(value);
	},
	
	/**
	 * is a valid dutch postcode (eg. 9999 AA)
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsPostcode: function(value)
	{
	    return value.match(/^[1-9][0-9]{3} ?[a-zA-Z]{2}$/);
	},
	
	/**
	 * same as IsPostcode, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsPostcode: function(value)
	{
	   return value.length == 0 || this.IsPostcode(value);
	},
	
	/**
	 * is a valid dutch phone-number
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsPhone: function(value)
	{
	    regex = /^[0-9]{2,4}[-]?[0-9]{6,8}$/;
	    value = value.replace(' ','');
		value = value.replace('-','');
	    return value.length == 10 && value.match(regex);
	},
	
	/**
	 * same as IsPhone, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsPhone: function(value)
	{
	    return value.length == 0 || this.IsPhone(value);
	},

	/**
	 * check if it's a valid ip adres
	 * 
	 * @param {string} value
	 * @return bool
	 */
	IsIp: function(ip )
	{
		return ip.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}:?\d*$/);
	},
	
	/**
	 * same as IsIp, only now the value is also valid if it is empty
	 *
	 * @param string value
	 * @return bool
	 */
	_IsIp: function(ip )
	{
		return value.length == 0 || this.IsIp(value);
	}
	
}
